/*
 * Copyright 2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @file    oes_client_my_spi_sample.c
 *
 * OES API接口库的CPP风格客户端示例。客户需要实现自己的SPI定义
 *
 * @version 1.0 2017/08/24
 * @since   2017/08/24
 */


#include    "oes_client_my_spi_sample.h"
#include "strategy_engine.h"

/* 连接异常断开回调函数 */
void
OesClientMySpi::OnDisconnected(int32 errorCode, eOesApiChannelTypeT channelType) {
    switch (channelType) {
        case OESAPI_CHANNEL_TYPE_ORDER:         /* 委托通道 */
            LOG4CPLUS_ERROR(MY_LOGGER, LOG4CPLUS_TEXT("channel order disconnected: ") << errorCode);
            break;
        case OESAPI_CHANNEL_TYPE_REPORT:        /* 回报通道 */
            LOG4CPLUS_ERROR(MY_LOGGER, LOG4CPLUS_TEXT("channel report disconnected: ") << errorCode);
            break;
        case OESAPI_CHANNEL_TYPE_QUERY:         /* 查询通道 */
            LOG4CPLUS_ERROR(MY_LOGGER, LOG4CPLUS_TEXT("channel query disconnected: ") << errorCode);
            break;
        default:
            LOG4CPLUS_ERROR(MY_LOGGER, LOG4CPLUS_TEXT("channel unkonw disconnected: ") << errorCode);
            break;
    }
}

/* 委托业务拒绝回报 */
void
OesClientMySpi::OnBusinessReject(int32 errorCode, const OesOrdRejectT *pOrderReject) {

    if (pOrderReject->bsType != OES_BS_TYPE_CANCEL) {
        // 委托
        SiriusTradeRspOrderRejectedField order(
                m_order_reference_system_order_no[common_function::env_client_seq_no(pOrderReject->clEnvId,
                                                                                     pOrderReject->clSeqNo)],
                errorCode);
        m_trade_engine->m_trade_queue_slow.enqueue(SiriusQueueField(order));
    } else {
        // 撤单
        SiriusTradeRspOrderCancelRejectedField order(
                m_order_reference_system_order_no[common_function::env_client_seq_no(pOrderReject->origClEnvId,
                                                                                     pOrderReject->origClSeqNo)],
                errorCode);
        m_trade_engine->m_trade_queue_slow.enqueue(SiriusQueueField(order));
    }
}

/* 委托已收回报 */
void
OesClientMySpi::OnOrderInsert(const OesOrdCnfmT *pOrderInsert) {

    if (pOrderInsert->bsType != OES_BS_TYPE_CANCEL) {
        // 委托
        m_trade_engine->m_trade_queue_slow.enqueue(SiriusQueueField(
                SiriusTradeRspOrderReceivedField(
                        m_order_reference_system_order_no[common_function::env_client_seq_no(pOrderInsert->clEnvId,
                                                                                             pOrderInsert->clSeqNo)],
                        pOrderInsert->clOrdId)));
    } else {
        // 撤单
        m_trade_engine->m_trade_queue_slow.enqueue(SiriusQueueField(
                SiriusTradeRspOrderCancelReceivedField(
                        m_order_reference_system_order_no[common_function::env_client_seq_no(pOrderInsert->origClEnvId,
                                                                                             pOrderInsert->origClSeqNo)])));
    }
}

static int order_status(int status) {
    if (status == 8) return SIRIUS_ORDER_STATUS_FILLED;
    if (status == 3) return SIRIUS_ORDER_STATUS_PART_FILLED;
    if (status == 6) return SIRIUS_ORDER_STATUS_PART_FILLED_WITHDRAW;
    if (status == 2) return SIRIUS_ORDER_STATUS_STOCK_QUEUE;
    if (status == 5) return SIRIUS_ORDER_STATUS_STOCK_SENT_CANCELING;
    if (status == 7) return SIRIUS_ORDER_STATUS_CANCELED;
    if (status == 0) return SIRIUS_ORDER_STATUS_UN_KNOW;

    return SIRIUS_ORDER_STATUS_UN_KNOW;
}

/* 委托确认回报 */
void
OesClientMySpi::OnOrderReport(int32 errorCode, const OesOrdCnfmT *pOrderReport) {

    if (pOrderReport->bsType != OES_BS_TYPE_CANCEL) {
        m_trade_engine->m_trade_queue_slow.enqueue(SiriusQueueField(
                SiriusTradeRspOrderField(
                        m_order_reference_system_order_no[common_function::env_client_seq_no(pOrderReport->clEnvId,
                                                                                             pOrderReport->clSeqNo)],
                        pOrderReport->clOrdId, pOrderReport->ordQty, pOrderReport->ordPrice,
                        pOrderReport->cumQty, pOrderReport->cumAmt, pOrderReport->ordStatus)));
    }
}

/* 成交回报 */
void
OesClientMySpi::OnTradeReport(const OesTrdCnfmT *pTradeReport) {

    m_trade_engine->m_trade_queue_slow.enqueue(SiriusQueueField(
            SiriusTradeRspOrderDealField(
                    m_order_reference_system_order_no[common_function::env_client_seq_no(pTradeReport->clEnvId,
                                                                                         pTradeReport->clSeqNo)],
                    pTradeReport->clOrdId, pTradeReport->trdQty,
                    pTradeReport->trdPrice)));
}

/* 资金变动回报 */
void
OesClientMySpi::OnCashAssetVariation(const OesCashAssetItemT *pCashAssetItem) {

}


/* 持仓变动回报 */
void
OesClientMySpi::OnStockHoldingVariation(const OesStkHoldingItemT *pStkHoldingItem) {
    m_trade_engine->m_trade_queue_slow.enqueue(SiriusQueueField(
            SiriusTradeRspHoldingField(pStkHoldingItem->securityId, pStkHoldingItem->sellAvlHld,
                                       pStkHoldingItem->originalHld)));
}

/* 出入金委托拒绝回报 */
void
OesClientMySpi::OnFundTrsfReject(int32 errorCode,
                                 const OesFundTrsfRejectT *pFundTrsfReject) {

}


/* 出入金委托执行回报 */
void
OesClientMySpi::OnFundTrsfReport(int32 errorCode,
                                 const OesFundTrsfReportT *pFundTrsfReport) {

}


/* 市场状态信息 */
void
OesClientMySpi::OnMarketState(const OesMarketStateItemT *pMarketStateItem) {

}


/* 查询到委托信息回调 */
void
OesClientMySpi::OnQueryOrder(const OesOrdItemT *pOrder,
                             const OesQryCursorT *pCursor, int32 requestId) {

}


/* 查询到成交信息回调 */
void
OesClientMySpi::OnQueryTrade(const OesTrdItemT *pTrade,
                             const OesQryCursorT *pCursor, int32 requestId) {


}


/* 查询到资金信息回调 */
void
OesClientMySpi::OnQueryCashAsset(const OesCashAssetItemT *pCashAsset,
                                 const OesQryCursorT *pCursor, int32 requestId) {
    if(pCursor->isEnd)
    {
        SiriusTradeRspBalanceField f;
        f.currentAvailableBal = pCashAsset->currentAvailableBal;
        f.currentDrawableBal = pCashAsset->currentDrawableBal;
        f.currentTotalBal = pCashAsset->currentTotalBal;
        m_trade_engine->m_trade_queue_slow.enqueue(SiriusQueueField(f));
    }
}


/* 查询到持仓信息回调 */
void
OesClientMySpi::OnQueryStkHolding(const OesStkHoldingItemT *pStkHolding,
                                  const OesQryCursorT *pCursor, int32 requestId) {
    SiriusTradeRspHoldingField obj;
    obj.symbol = pStkHolding->securityId;
    obj.available_volume = pStkHolding->sellAvlHld;
    obj.history_volume = pStkHolding->originalHld;
    obj.costPrice = pStkHolding->costPrice;
    obj.sumHld = pStkHolding->sumHld;

    m_holdings.emplace_back(obj);

    if (pCursor->isEnd) {
        m_trade_engine->m_trade_queue_slow.enqueue(SiriusQueueField(m_holdings));
        m_holdings.clear();
    }
}


/* 查询到配号、中签信息回调 */
void
OesClientMySpi::OnQueryLotWinning(const OesLotWinningItemT *pLotWinning,
                                  const OesQryCursorT *pCursor, int32 requestId) {
    fprintf(stdout, ">>> 查询到新股配号、中签信息: index[%d], isEnd[%c], " \
            "股东账户代码[%s], 证券代码[%s], 证券名称[%s], " \
            "市场代码[%" __SPK_FMT_HH__ "u], " \
            "记录类型[%" __SPK_FMT_HH__ "u], " \
            "失败原因[%" __SPK_FMT_HH__ "u], 配号、中签日期[%08d], " \
            "配号首个号码[%" __SPK_FMT_LL__ "d], 配号成功数量/中签股数[%d], " \
            "最终发行价[%d], 中签金额[%" __SPK_FMT_LL__ "d]\n",
            pCursor->seqNo, pCursor->isEnd ? 'Y' : 'N',
            pLotWinning->invAcctId, pLotWinning->securityId,
            pLotWinning->securityName, pLotWinning->mktId, pLotWinning->lotType,
            pLotWinning->rejReason, pLotWinning->lotDate, pLotWinning->assignNum,
            pLotWinning->lotQty, pLotWinning->lotPrice, pLotWinning->lotAmt);
}


/* 查询到客户信息回调 */
void
OesClientMySpi::OnQueryCustInfo(const OesCustItemT *pCust,
                                const OesQryCursorT *pCursor, int32 requestId) {
    fprintf(stdout, ">>> 查询到客户信息: index[%d], isEnd[%c], " \
            "客户ID[%s], 客户类型[%" __SPK_FMT_HH__ "u], " \
            "客户状态[%" __SPK_FMT_HH__ "u], 风险评级[%" __SPK_FMT_HH__ "u], " \
            "机构标志[%" __SPK_FMT_HH__ "u], 投资者分类[%c]\n",
            pCursor->seqNo, pCursor->isEnd ? 'Y' : 'N',
            pCust->custId, pCust->custType, pCust->status,
            pCust->riskLevel, pCust->institutionFlag,
            pCust->investorClass == OES_INVESTOR_CLASS_NORMAL ?
            '0' : pCust->investorClass + 'A' - 1);
}


/* 查询到股东账户信息回调 */
void
OesClientMySpi::OnQueryInvAcct(const OesInvAcctItemT *pInvAcct,
                               const OesQryCursorT *pCursor, int32 requestId) {
    fprintf(stdout, ">>> 查询到证券账户信息: index[%d], isEnd[%c], " \
            "证券账户[%s], 市场代码[%" __SPK_FMT_HH__ "u], " \
            "客户代码[%s], 账户状态[%" __SPK_FMT_HH__ "u], " \
            "主板权益[%d], 科创板权益[%d]\n",
            pCursor->seqNo, pCursor->isEnd ? 'Y' : 'N',
            pInvAcct->invAcctId, pInvAcct->mktId,
            pInvAcct->custId, pInvAcct->status,
            pInvAcct->subscriptionQuota, pInvAcct->kcSubscriptionQuota);
}


/* 查询到佣金信息回调 */
void
OesClientMySpi::OnQueryCommissionRate(
        const OesCommissionRateItemT *pCommissionRate,
        const OesQryCursorT *pCursor, int32 requestId) {
    fprintf(stdout, ">>> 查询到佣金信息: index[%d], isEnd[%c], " \
            "客户代码[%s], 证券代码[%s], 市场代码[%" __SPK_FMT_HH__ "u], " \
            "证券类型[%" __SPK_FMT_HH__ "u], 证券子类型[%" __SPK_FMT_HH__ "u], " \
            "买卖类型[%" __SPK_FMT_HH__ "u], 币种[%" __SPK_FMT_HH__ "u], " \
            "费用标识[%" __SPK_FMT_HH__ "u], 计算模式 [%" __SPK_FMT_HH__ "u], " \
            "费率[%" __SPK_FMT_LL__ "d], 最低费用[%d], 最高费用[%d]\n",
            pCursor->seqNo, pCursor->isEnd ? 'Y' : 'N',
            pCommissionRate->custId, pCommissionRate->securityId,
            pCommissionRate->mktId, pCommissionRate->securityType,
            pCommissionRate->subSecurityType, pCommissionRate->bsType,
            pCommissionRate->currType, pCommissionRate->feeType,
            pCommissionRate->calcFeeMode, pCommissionRate->feeRate,
            pCommissionRate->minFee, pCommissionRate->maxFee);
}


/* 查询到出入金流水信息回调 */
void
OesClientMySpi::OnQueryFundTransferSerial(
        const OesFundTransferSerialItemT *pFundTrsf,
        const OesQryCursorT *pCursor, int32 requestId) {
    fprintf(stdout, ">>> 查询到出入金流水: index[%d], isEnd[%c], " \
            "客户端环境号[%" __SPK_FMT_HH__ "d], 客户委托流水号[%d], " \
            "资金账户[%s], 方向[%s], 金额[%" __SPK_FMT_LL__ "d], " \
            "出入金状态[%" __SPK_FMT_HH__ "u], 错误原因[%d], 主柜错误码[%d], " \
            "错误信息[%s], 柜台委托编码[%d], 接收日期[%08d], " \
            "接收时间[%09d], 上报时间[%09d], 完成时间[%09d]\n",
            pCursor->seqNo, pCursor->isEnd ? 'Y' : 'N',
            pFundTrsf->clEnvId, pFundTrsf->clSeqNo,
            pFundTrsf->cashAcctId,
            pFundTrsf->direct == OES_FUND_TRSF_DIRECT_IN ? "Bank->Broker" : "Broker->Bank",
            pFundTrsf->occurAmt, pFundTrsf->trsfStatus, pFundTrsf->rejReason,
            pFundTrsf->counterErrCode, pFundTrsf->errorInfo,
            pFundTrsf->counterEntrustNo, pFundTrsf->operDate,
            pFundTrsf->operTime, pFundTrsf->dclrTime,
            pFundTrsf->doneTime);
}


/* 查询到证券发行信息回调 */
void
OesClientMySpi::OnQueryIssue(const OesIssueItemT *pIssue,
                             const OesQryCursorT *pCursor, int32 requestId) {
    fprintf(stdout, ">>> 查询到证券发行产品信息: index[%d], isEnd[%c], " \
            "证券代码[%s], 证券名称[%s], 正股代码[%s], " \
            "市场代码[%" __SPK_FMT_HH__ "u], " \
            "证券类型[%" __SPK_FMT_HH__ "u], " \
            "证券子类型[%" __SPK_FMT_HH__ "u], " \
            "是否允许撤单[%" __SPK_FMT_HH__ "u], " \
            "是否允许重复认购[%" __SPK_FMT_HH__ "u], " \
            "发行起始时间[%d], 发行结束时间[%d], " \
            "发行总量[%" __SPK_FMT_LL__ "d], " \
            "份数单位[%d], 最大份数[%d], 最小份数[%d], " \
            "发行价格[%d], 价格上限[%d], 价格下限[%d]\n",
            pCursor->seqNo, pCursor->isEnd ? 'Y' : 'N',
            pIssue->securityId, pIssue->securityName,
            pIssue->underlyingSecurityId, pIssue->mktId, pIssue->securityType,
            pIssue->subSecurityType, pIssue->isCancelAble,
            pIssue->isReApplyAble, pIssue->startDate, pIssue->endDate,
            pIssue->issueQty, pIssue->qtyUnit, pIssue->ordMaxQty,
            pIssue->ordMinQty, pIssue->issuePrice, pIssue->ceilPrice,
            pIssue->floorPrice);
}


/* 查询到证券信息回调 */
void
OesClientMySpi::OnQueryStock(const OesStockItemT *pStock,
                             const OesQryCursorT *pCursor, int32 requestId) {
    fprintf(stdout, ">>> 查询到现货产品信息: index[%d], isEnd[%c], " \
            "证券代码[%s], 证券名称[%s], 基金代码[%s], " \
            "市场代码[%" __SPK_FMT_HH__ "u], 证券类型[%" __SPK_FMT_HH__ "u], " \
            "证券子类型[%" __SPK_FMT_HH__ "u], 证券级别[%" __SPK_FMT_HH__ "u], " \
            "风险等级[%" __SPK_FMT_HH__ "u], 停牌标志[%" __SPK_FMT_HH__ "u], " \
            "适当性管理[%" __SPK_FMT_HH__ "u], 当日回转[%" __SPK_FMT_HH__ "u], " \
            "价格单位[%d], 买份数单位[%d], 卖份数单位[%d], " \
            "昨日收盘价[%d], 债券利息[%" __SPK_FMT_LL__ "d], " \
            "涨停价[%d], 跌停价[%d]\n",
            pCursor->seqNo, pCursor->isEnd ? 'Y' : 'N',
            pStock->securityId, pStock->securityName, pStock->fundId,
            pStock->mktId, pStock->securityType, pStock->subSecurityType,
            pStock->securityLevel, pStock->securityRiskLevel, pStock->suspFlag,
            pStock->qualificationClass, pStock->isDayTrading,
            pStock->priceUnit, pStock->buyQtyUnit, pStock->sellQtyUnit,
            pStock->prevClose, pStock->bondInterest,
            pStock->priceLimit[OES_TRD_SESS_TYPE_T].ceilPrice,
            pStock->priceLimit[OES_TRD_SESS_TYPE_T].floorPrice);
}


/* 查询到ETF产品信息回调 */
void
OesClientMySpi::OnQueryEtf(const OesEtfItemT *pEtf,
                           const OesQryCursorT *pCursor, int32 requestId) {
    fprintf(stdout, ">>> 查询到ETF申赎产品信息: index[%d], isEnd[%c], " \
            "基金代码[%s], 证券代码[%s], " \
            "市场代码[%" __SPK_FMT_HH__ "u], " \
            "申购赎回单位[%d], 最大现金替代比例[%d], " \
            "前一日最小申赎单位净值[%" __SPK_FMT_LL__ "d], " \
            "前一日现金差额[%" __SPK_FMT_LL__ "d], " \
            "成分证券数目[%d]\n",
            pCursor->seqNo, pCursor->isEnd ? 'Y' : 'N',
            pEtf->fundId, pEtf->securityId, pEtf->mktId, pEtf->creRdmUnit,
            pEtf->maxCashRatio, pEtf->navPerCU, pEtf->cashCmpoent,
            pEtf->componentCnt);
}


/* 查询到ETF成分股信息回调 */
void
OesClientMySpi::OnQueryEtfComponent(const OesEtfComponentItemT *pEtfComponent,
                                    const OesQryCursorT *pCursor, int32 requestId) {
    fprintf(stdout, ">>> 查询到ETF成分股信息: index[%d], isEnd[%c], " \
            "成分证券代码[%s], 市场代码[%" __SPK_FMT_HH__ "u], " \
            "现金替代标识[%d], 昨日收盘价[%d], 成分证券数量[%d], " \
            "溢价比例[%d], 申购替代金额[%" __SPK_FMT_LL__ "d], " \
            "赎回替代金额[%" __SPK_FMT_LL__ "d]\n",
            pCursor->seqNo, pCursor->isEnd ? 'Y' : 'N',
            pEtfComponent->securityId, pEtfComponent->mktId,
            pEtfComponent->subFlag, pEtfComponent->prevClose,
            pEtfComponent->qty, pEtfComponent->premiumRate,
            pEtfComponent->creationSubCash, pEtfComponent->redemptionCashSub);
}


/* 查询到市场状态信息回调 */
void
OesClientMySpi::OnQueryMarketState(const OesMarketStateItemT *pMarketState,
                                   const OesQryCursorT *pCursor, int32 requestId) {
    fprintf(stdout, ">>> 查询到市场状态信息: index[%d], isEnd[%c], " \
            "交易所代码[%hhu], 交易平台类型[%hhu], 市场类型[%hhu], " \
            "市场状态[%hhu]\n", \
            pCursor->seqNo, pCursor->isEnd ? 'Y' : 'N',
            pMarketState->exchId, pMarketState->platformId,
            pMarketState->mktId, pMarketState->mktState);
}

OesClientMySpi::OesClientMySpi() = default;

OesClientMySpi::~OesClientMySpi() {
    /* do nothing */
}

void OesClientMySpi::SetTradeEngine(trade_engine *engine) {
    m_trade_engine = engine;
}